Bringing the Language Server Protocol to Jupyter(Lab)

.. rubric:: ⸻ @krassowski, @bollwyvl :name: krassowski-bollwyvl

What is the Language Server Protocol?

A Language Server is meant to provide the language-specific smarts and communicate with **development tools* over a protocol that enables inter-process communication.*

The idea behind the Language Server Protocol (LSP) is to standardize the protocol for how such **servers* and development tools communicate. This way, a single Language Server can be re-used in multiple development tools, which in turn can support multiple languages with minimal effort.*

microsoft.github.io/language-server-protocol

*emphasis ours*

But Practically…

  • markdown specification (on the above site)

  • reference server, vscode-language-server-node

  • reference client, vscode

  • number of other clients

What is a Language Server?

  • a process that speaks JSON-RPC 2.0 over stdin/stdout (or sometimes TCP)

  • the messages are things like

  • 🙋 I opened a file!

    • 🤖 YOUR FILE HAS TRAILING WHITESPACE

  • 🙋 I fixed some whitespace

    • 🤖 THERE IS NOTHING WRONG NOW

  • 🙋 I hovered over a symbol!

    • 🤖 IT’S A FUNCTION

  • 🙋 Where did it come from?

    • 🤖 THIS OTHER FILE OVER HERE

Sounds a lot like the Jupyter Message Spec

  • Yep

  • But it’s good to have both

  • Kernel (usually) does dynamic analysis, so can do More Things

  • Language Server (usually) does static analysis, so (usually) faster

    • Also, it doesn’t make sense to have Kernels for some things

  • Future kernels could contribute their own LSP messages

SPOOKY DEMO

[6]:
pumpkins = "🎃" * 10
pumpkins + "more pumpkins"
[6]:
'🎃🎃🎃🎃🎃🎃🎃🎃🎃🎃more pumpkins'
[ ]:
pumpkins + 2
[8]:
Chicken = 0
chicken = {
    chicken for chicken in range(10)
    if chicken in [Chicken]
}
[10]:
# this_variable_is_not_used - 1

What have we done on [@krassowski/jupyterlab-lsp](https://github.com/krassowski/jupyterlab-lsp)

  • Reuse existing LSP <-> CodeMirror bridge

  • Implement Jump to Definition

  • TypeScript Unit tests on Travis CI

  • Traitnado-based LSP WebSocket proxy

  • Python Unit tests and Browser tests on Azure Pipelines

  • Upstream PRs

  • yaml-language-server

  • conda-forge (for r-languageserver)

  • void on

Next Steps

  • Keep putting out releases people can try out

  • Keep heading towards proposal to JupyterLab core

  • Encourage some contributors

  • Add/Test more Language Servers (and Features)

  • Warm up the JEP PR #26

[ ]:
%%file spooky.csscss
@import url('https://fonts.googleapis.com/css?family=Lakki+Reddy&display=swap');
#main {
  --jp-border-width: 0.01px;
  --pumpkin: rgba(245, 124, 0, 0.75);
  --ghost: rgba(255, 255, 255, 0.5);
}
#main .cm-lsp-highlight:not(:empty) {
  background-color: var(--pumpkin);
  border-radius: 5px;
  color: yellow !important;
  text-shadow: 0 0 5px yellow;
  padding: 0.25em 0;
  font-weight: bold;
  position: relative;
  border-left: none;
  border-right: none;
  transition: all 1s;
}
.cm-lsp-highlight:not(:empty)::before {
  content: '🍃';
  font-size: 0.9em;
  top: -0.9em;
  left: 50%;
  color: green;
  position: absolute;
  text-shadow: none;
  transition: all 1s;
}
#main
  .cm-lsp-diagnostic-Error:not(.cm-lsp-highlight):not(.cm-lsp-highlight-Write) {
  color: black !important;
  background-color: var(--ghost);
  border-radius: 1em 1em 0 0;
  font-weight: bold;
  padding: 0.25em 0.25em 0.1em 0.25em;
  position: relative;
  text-decoration-color: transparent;
  margin: 0 0.2em;
  transition: all 1s;
}
.cm-lsp-diagnostic-Error:not(.cm-lsp-highlight):not(.cm-lsp-highlight-Write):before {
  position: absolute;
  left: 0;
  right: 0;
  top: -10%;
  height: 110%;
  border-bottom: dotted 2px var(--ghost) !important;
  content: '';
  transition: all 1s;
}
#main
  .cm-lsp-diagnostic-Warning:not(.cm-lsp-highlight-Write):not(.cm-lsp-diagnostic-Error) {
  position: relative;
  color: green;
  font-weight: bold;
  text-shadow: 0 0 10px green;
  text-decoration: none;
  font-size: 2em;
  -webkit-text-stroke: 1px rgba(0, 50, 0, 0.5);
  font-family: 'Lakki Reddy', cursive;
  transition: all 1s;
}
body .jp-HoverBox {
  min-width: 300px;
  overflow-y: auto;
}
body .jp-HoverBox,
body .jp-HoverBox * {
  background-color: black !important;
  color: red !important;
  font-family: 'Lakki Reddy', cursive;
  font-size: 16px !important;
  text-align: center;
  font-weight: bold;
}
[2]:
import pathlib
import IPython
css = pathlib.Path("spooky.css").read_text()
IPython.display.HTML(f"<style>{css}</style>")
[2]: